/*
 Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
 Original sources are available at www.latestbit.com

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 StdNetworkCommand.hpp. Created on: 08.09.2009 13:22:51
 */

#ifndef STDNETWORKCOMMAND_HPP_
#define STDNETWORKCOMMAND_HPP_

#include "smartobjects/SmartObjectsDefs.hpp"
#include "smartobjects/network/INetworkCommand.hpp"
#include "smartobjects/exceptions/DataTypesCoderException.hpp"

namespace smartobjects {

class StdNetworkCommand : public INetworkCommand {
public:
	SO_DLLPRFX static const unsigned char PROTOCOL_ID;
	SO_DLLPRFX static const unsigned char PROTOCOL_VERSION;
	SO_DLLPRFX static const unsigned int COMMAND_SIZE_POSITION;
	SO_DLLPRFX static const unsigned int GRAMMAR_SIZE_POSITION;
	SO_DLLPRFX static const unsigned int HEADER_SIZE;

public:
	SO_DLLPRFX StdNetworkCommand( unsigned char nCommandId, ByteBuffer* poToBuffer, IDataTypesCoder* poCoder);
	SO_DLLPRFX StdNetworkCommand( ByteBuffer* poFromBuffer, IDataTypesCoder* poCoder );
	SO_DLLPRFX StdNetworkCommand( const INetworkCommand* poBaseCommand);

	SO_DLLPRFX virtual ~StdNetworkCommand();

	SO_DLLPRFX virtual unsigned char getProtocolVersion() const;
	SO_DLLPRFX virtual unsigned char getCommandId() const;

	SO_DLLPRFX virtual const std::string& getDestination() const;
	SO_DLLPRFX virtual void setDestination(const std::string&);

	SO_DLLPRFX virtual const std::string& getInstanceId() const;
	SO_DLLPRFX virtual void setInstanceId(const std::string&);

	SO_DLLPRFX virtual boost::uint32_t getMessageId() const;
	SO_DLLPRFX virtual void setMessageId(boost::uint32_t);

	SO_DLLPRFX virtual RequestContext* getRequestContext() const;
	SO_DLLPRFX virtual void setRequestContext(RequestContext* );

	SO_DLLPRFX virtual unsigned int getHeaderSize() const;
	SO_DLLPRFX virtual unsigned int getCommandBodySize() const;

	SO_DLLPRFX virtual ByteBuffer* getBuffer() const ;
	SO_DLLPRFX virtual void setBuffer(ByteBuffer*) ;

	SO_DLLPRFX virtual bool isRequest() const ;
	SO_DLLPRFX virtual bool isResponse() const ;
	SO_DLLPRFX virtual bool isSerialized() const ;
	SO_DLLPRFX virtual bool isDeserialized() const ;
	SO_DLLPRFX virtual IDataTypesCoder* getDataTypesCoder() const;

	SO_DLLPRFX virtual void deserialize();
	SO_DLLPRFX virtual void beginSerialize();
	SO_DLLPRFX virtual void finishSerialize();

protected:
	SO_DLLPRFX virtual void decodeHeader();
	SO_DLLPRFX virtual void encodeHeader();

protected:
	unsigned char m_nProtocolVersion;
	unsigned char m_nCommandId;
	boost::uint32_t m_nMessageId;
	unsigned char m_nSectionsFlags;
	unsigned int m_nCommandBodySize;
	std::string m_sDestination;
	std::string m_sInstanceId;
	ByteBuffer* m_poBuffer;
	IDataTypesCoder* m_poCoder;
	bool m_bIsSerialized, m_bIsDeserialized;

	RequestContext* m_poRequestContext;

protected:
	template<typename T>
	void appendIntFieldHeader(T nValue) {
		T nBigEndianValue = OSEndianTools::host_to_network(nValue);
		unsigned char* pToInt = (unsigned char*)&nBigEndianValue;
		m_poBuffer->append( pToInt, sizeof(T) );
	}

	template<typename T>
	void replaceIntFieldHeader(T nValue, unsigned int nPos) {
		T nBigEndianValue = OSEndianTools::host_to_network(nValue);
		unsigned char* pToInt = (unsigned char*)&nBigEndianValue;
		if(nPos + sizeof(T) >= m_poBuffer->getLen())
			throw DataTypesCoderException("Internal error. Buffer overflow when replacing field in the header");
		memcpy( m_poBuffer->getData() + nPos, pToInt, sizeof(T));
	}

	template<typename T>
	T decodeIntFieldHeader(unsigned char** pBufferPtr) {
		T nResult = 0;
		memcpy ( (unsigned char*) &nResult, *pBufferPtr, sizeof(T) );
		nResult = OSEndianTools::network_to_host ( nResult );
		*pBufferPtr = *pBufferPtr + sizeof(T);
		return nResult;
	}
};

}

#endif /* STDNETWORKCOMMAND_HPP_ */
